package org.springframework.web.concurrent;

import org.springframework.web.common.RequestAttrs;
import org.springframework.web.modules.ModuleRenderResult;
import org.springframework.web.servlet.SmartDispatcherServlet.RenderTask;
import org.springframework.web.tag.templates.ScriptTemplate;
import org.springframework.web.util.CommonUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.logging.Logger;


/**
 * @author float.lu
 */
public class ConcurrentEngine {
    private static Logger LOG = Logger.getLogger(ConcurrentEngine.class.getName());
    private static ExecutorService service;

    public static String CUSTOMER_SERVICE_NAME = "concurrentEngineService";

    public void run(List<RenderTask> tasks) {
        CompletionService<ModuleRenderResult> completionService = new ExecutorCompletionService(service);
        CountDownLatch latch = new CountDownLatch(tasks.size());
        HttpServletResponse response = CommonUtil.getResponse();
        HttpServletRequest request = CommonUtil.getRequest();

        long maxTimeout = 0L;
        if (request.getAttribute(RequestAttrs.CO_TIMEOUT) != null) {
            maxTimeout = (Long)request.getAttribute(RequestAttrs.CO_TIMEOUT);
        }

        List<Future<ModuleRenderResult>> solvers = new ArrayList<Future<ModuleRenderResult>>(tasks.size());
        for (RenderTask renderTask : tasks)  {
            renderTask.setLatch(latch);
            Future<ModuleRenderResult> future = completionService.submit(renderTask);
            solvers.add(future);
        }

        try {
            for (int i=0 ; i < solvers.size() ; i++) {
                try {
                    if (maxTimeout == 0L) {
                        ModuleRenderResult renderResult = completionService.take().get();
                        write(response, renderResult);
                    } else {
                        ModuleRenderResult renderResult = completionService.poll(maxTimeout, TimeUnit.MILLISECONDS).get();
                        write(response, renderResult);
                    }
                } catch (Exception e) {
                    //NO OP
                }
            }
        } catch (Exception e) {
            //NO OP
            LOG.info("exception!");
        } finally {
            for (Future future : solvers) {
                future.cancel(true);
            }
        }
        try {
            response.getWriter().close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    private void write(HttpServletResponse response, ModuleRenderResult renderResult) {
        try {
            PrintWriter writer = response.getWriter();
            StringBuilder sb = new StringBuilder();
            sb.append(String.format(ScriptTemplate.callScription, CommonUtil.makeIdByMapping(renderResult.getModuleConfiguration().getMapping()), renderResult.getContent()));
            writer.write(sb.toString());
            writer.flush();
        } catch (Exception e) {
            //NO OP
        }
    }

    public static void setService(ExecutorService service) {
        if (service == null) {
            Executors.newCachedThreadPool();
        } else {
            ConcurrentEngine.service = service;
        }
    }

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                service.shutdown();
            }
        }));
    }
}
